home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 August: Tool Chest / Dev.CD Aug 94.toast / Sample Code / MoreFiles 1.1.1 / MoreFilesExtras.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-22  |  51.3 KB  |  1,714 lines  |  [TEXT/KAHL]

  1. /*
  2. **    Apple Macintosh Developer Technical Support
  3. **
  4. **    A collection of useful high-level File Manager routines.
  5. **
  6. **    by Jim Luther, Apple Developer Technical Support
  7. **
  8. **    File:        MoreFilesExtras.c
  9. **
  10. **    Copyright © 1992-1994 Apple Computer, Inc.
  11. **    All rights reserved.
  12. **
  13. **    You may incorporate this sample code into your applications without
  14. **    restriction, though the sample code has been provided "AS IS" and the
  15. **    responsibility for its operation is 100% yours.  However, what you are
  16. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  17. **    after having made changes. If you're going to re-distribute the source,
  18. **    we require that you make it clear in the source that the code was
  19. **    descended from Apple Sample Code, but that you've made changes.
  20. */
  21.  
  22. #ifndef __MOREFILESEXTRAS__
  23. #include "MoreFilesExtras.h"
  24. #endif
  25.  
  26. /*****************************************************************************/
  27.  
  28. /* local data structures */
  29.  
  30. /* The DeleteEnumGlobals structure is used to minimize the amount of
  31. ** stack space used when recursively calling DeleteLevel and to hold
  32. ** global information that might be needed at any time. */
  33. struct DeleteEnumGlobals
  34. {
  35.     OSErr            error;                /* temporary holder of results - saves 2 bytes of stack each level */
  36.     Str63            itemName;            /* the name of the current item */
  37.     UniversalFMPB    myPB;                /* the parameter block used for PBGetCatInfo calls */
  38. };
  39. typedef struct DeleteEnumGlobals DeleteEnumGlobals;
  40. typedef DeleteEnumGlobals *DeleteEnumGlobalsPtr;
  41.  
  42. /*****************************************************************************/
  43.  
  44. /* static prototypes */
  45.  
  46. static    void    pcpy(StringPtr d,
  47.                      StringPtr s);
  48.                      
  49. static    Ptr    GetSearchBuffer(long *buffSize);
  50.  
  51. static    void    DeleteLevel(long dirToDelete,
  52.                             DeleteEnumGlobalsPtr theGlobals);
  53.  
  54. /*****************************************************************************/
  55.  
  56. /* Copy a Pascal-string. */
  57. static    void    pcpy(StringPtr d,
  58.                      StringPtr s)
  59. {
  60.     short    i;
  61.  
  62.     i = *s;
  63.     do {
  64.         d[i] = s[i];
  65.     } while (i--);
  66. }
  67.  
  68. /*****************************************************************************/
  69.  
  70. /* A utility routine called by NameFileSearch and CreatorTypeFileSearch to
  71. ** allocate a optimization buffer. */
  72.  
  73. static    Ptr    GetSearchBuffer(long *buffSize)
  74. {
  75.     Ptr    tempPtr;
  76.     
  77.     *buffSize = 0x00004000;    /* start by trying for a 16K buffer */
  78.     while (*buffSize) {        /* and keep trying until *buffSize is zero */
  79.         tempPtr = NewPtr(*buffSize);
  80.         if (tempPtr)
  81.             break;    /* got it */
  82.         else
  83.             *buffSize -= 0x00000400;    /* didn't get it, subtract 1K and try again */
  84.     }
  85.     return (tempPtr);
  86. }
  87.  
  88. /*****************************************************************************/
  89.  
  90. pascal    OSErr    NameFileSearch(StringPtr volName,
  91.                                short vRefNum,
  92.                                const Str255 fileName,
  93.                                FSSpecPtr matches,
  94.                                long reqMatchCount,
  95.                                long *actMatchCount,
  96.                                Boolean newSearch,
  97.                                Boolean partial)
  98. {
  99.     CInfoPBRec        searchInfo1, searchInfo2;
  100.     HParamBlockRec    pb;
  101.     OSErr            error;
  102.     static CatPositionRec catPosition;
  103.     static short    lastVRefNum = 0;
  104.     
  105.     /* get the real volume reference number */
  106.     error = DetermineVRefNum(volName, vRefNum, &vRefNum);
  107.     if (error != noErr)
  108.         return (error);
  109.     
  110.     pb.csParam.ioNamePtr = nil;
  111.     pb.csParam.ioVRefNum = vRefNum;
  112.     pb.csParam.ioMatchPtr = matches;
  113.     pb.csParam.ioReqMatchCount = reqMatchCount;
  114.     pb.csParam.ioSearchBits = (partial) ?    /* tell CatSearch what we're looking for: */
  115.         (fsSBPartialName + fsSBFlAttrib) :     /* partial name file matches or */
  116.         (fsSBFullName + fsSBFlAttrib);        /* full name file matches */
  117.     pb.csParam.ioSearchInfo1 = &searchInfo1;
  118.     pb.csParam.ioSearchInfo2 = &searchInfo2;
  119.     pb.csParam.ioSearchTime = 0;
  120.     if ((newSearch) ||                /* If caller specified new search */
  121.         (lastVRefNum != vRefNum))    /* or if last search was to another volume, */
  122.         catPosition.initialize = 0;    /* then search from beginning of catalog */
  123.     pb.csParam.ioCatPosition = catPosition;
  124.     pb.csParam.ioOptBuffer = GetSearchBuffer(&pb.csParam.ioOptBufSize);
  125.  
  126.     /* search for fileName */
  127.     searchInfo1.hFileInfo.ioNamePtr = (StringPtr)fileName;
  128.     searchInfo2.hFileInfo.ioNamePtr = nil;
  129.     
  130.     /* only match files (not directories) */
  131.     searchInfo1.hFileInfo.ioFlAttrib = 0x00;
  132.     searchInfo2.hFileInfo.ioFlAttrib = 0x10;
  133.  
  134.     error = PBCatSearchSync((CSParamPtr)&pb);
  135.     
  136.     if ((error == noErr) ||                            /* If no errors or the end of catalog was */
  137.         (error == eofErr))                            /* found, then the call was successful so */
  138.         *actMatchCount = pb.csParam.ioActMatchCount;    /* return the match count */
  139.     else
  140.         *actMatchCount = 0;                            /* else no matches found */
  141.     
  142.     if ((error == noErr) ||                            /* If no errors */
  143.         (error == catChangedErr))                    /* or there was a change in the catalog */
  144.     {
  145.         catPosition = pb.csParam.ioCatPosition;
  146.         lastVRefNum = vRefNum;
  147.             /* we can probably start the next search where we stopped this time */
  148.     }
  149.     else
  150.         catPosition.initialize = 0;
  151.             /* start the next search from beginning of catalog */
  152.     
  153.     if (pb.csParam.ioOptBuffer)
  154.         DisposPtr(pb.csParam.ioOptBuffer);
  155.         
  156.     return (error);
  157. }
  158.  
  159. /*****************************************************************************/
  160.  
  161. pascal    OSErr    CreatorTypeFileSearch(StringPtr volName,
  162.                                       short vRefNum,
  163.                                       OSType creator,
  164.                                       OSType fileType,
  165.                                       FSSpecPtr matches,
  166.                                       long reqMatchCount,
  167.                                       long *actMatchCount,
  168.                                       Boolean newSearch)
  169. {
  170.     CInfoPBRec        searchInfo1, searchInfo2;
  171.     HParamBlockRec    pb;
  172.     OSErr            error;
  173.     static CatPositionRec catPosition;
  174.     static short    lastVRefNum = 0;
  175.     
  176.     /* get the real volume reference number */
  177.     error = DetermineVRefNum(volName, vRefNum, &vRefNum);
  178.     if (error != noErr)
  179.         return (error);
  180.     
  181.     pb.csParam.ioNamePtr = nil;
  182.     pb.csParam.ioVRefNum = vRefNum;
  183.     pb.csParam.ioMatchPtr = matches;
  184.     pb.csParam.ioReqMatchCount = reqMatchCount;
  185.     pb.csParam.ioSearchBits = fsSBFlAttrib + fsSBFlFndrInfo;    /* Looking for finder info file matches */
  186.     pb.csParam.ioSearchInfo1 = &searchInfo1;
  187.     pb.csParam.ioSearchInfo2 = &searchInfo2;
  188.     pb.csParam.ioSearchTime = 0;
  189.     if ((newSearch) ||                /* If caller specified new search */
  190.         (lastVRefNum != vRefNum))    /* or if last search was to another volume, */
  191.         catPosition.initialize = 0;    /* then search from beginning of catalog */
  192.     pb.csParam.ioCatPosition = catPosition;
  193.     pb.csParam.ioOptBuffer = GetSearchBuffer(&pb.csParam.ioOptBufSize);
  194.  
  195.     /* no fileName */
  196.     searchInfo1.hFileInfo.ioNamePtr = nil;
  197.     searchInfo2.hFileInfo.ioNamePtr = nil;
  198.     
  199.     /* only match files (not directories) */
  200.     searchInfo1.hFileInfo.ioFlAttrib = 0x00;
  201.     searchInfo2.hFileInfo.ioFlAttrib = 0x10;
  202.     
  203.     /* search for creator; if creator = 0x00000000, ignore creator */
  204.     searchInfo1.hFileInfo.ioFlFndrInfo.fdCreator = creator;
  205.     searchInfo2.hFileInfo.ioFlFndrInfo.fdCreator = (creator == (OSType)0x00000000) ?
  206.         (OSType)0x00000000 :
  207.         (OSType)0xffffffff;
  208.     
  209.     /* search for fileType; if fileType = 0x00000000, ignore fileType */
  210.     searchInfo1.hFileInfo.ioFlFndrInfo.fdType = fileType;
  211.     searchInfo2.hFileInfo.ioFlFndrInfo.fdType = (fileType == (OSType)0x00000000) ?
  212.         (OSType)0x00000000 :
  213.         (OSType)0xffffffff;
  214.     
  215.     /* zero all other FInfo fields */
  216.     searchInfo1.hFileInfo.ioFlFndrInfo.fdFlags = 0;
  217.     searchInfo1.hFileInfo.ioFlFndrInfo.fdLocation.v = 0;
  218.     searchInfo1.hFileInfo.ioFlFndrInfo.fdLocation.h = 0;
  219.     searchInfo1.hFileInfo.ioFlFndrInfo.fdFldr = 0;
  220.     
  221.     searchInfo2.hFileInfo.ioFlFndrInfo.fdFlags = 0;
  222.     searchInfo2.hFileInfo.ioFlFndrInfo.fdLocation.v = 0;
  223.     searchInfo2.hFileInfo.ioFlFndrInfo.fdLocation.h = 0;
  224.     searchInfo2.hFileInfo.ioFlFndrInfo.fdFldr = 0;
  225.  
  226.     error = PBCatSearchSync((CSParamPtr)&pb);
  227.     
  228.     if ((error == noErr) ||                            /* If no errors or the end of catalog was */
  229.         (error == eofErr))                            /* found, then the call was successful so */
  230.         *actMatchCount = pb.csParam.ioActMatchCount;    /* return the match count */
  231.     else
  232.         *actMatchCount = 0;                            /* else no matches found */
  233.     
  234.     if ((error == noErr) ||                            /* If no errors */
  235.         (error == catChangedErr))                    /* or there was a change in the catalog */
  236.     {
  237.         catPosition = pb.csParam.ioCatPosition;
  238.         lastVRefNum = vRefNum;
  239.             /* we can probably start the next search where we stopped this time */
  240.     }
  241.     else
  242.         catPosition.initialize = 0;
  243.             /* start the next search from beginning of catalog */
  244.     
  245.     if (pb.csParam.ioOptBuffer)
  246.         DisposPtr(pb.csParam.ioOptBuffer);
  247.         
  248.     return (error);
  249. }
  250.  
  251. /*****************************************************************************/
  252.  
  253. pascal    OSErr    DetermineVRefNum(StringPtr pathname,
  254.                                  short vRefNum,
  255.                                  short *realVRefNum)
  256. {
  257.     HParamBlockRec pb;
  258.     Str255 tempPathname;
  259.     OSErr error;
  260.  
  261.     pb.volumeParam.ioVRefNum = vRefNum;
  262.     if (pathname == nil) {
  263.         pb.volumeParam.ioNamePtr = nil;
  264.         pb.volumeParam.ioVolIndex = 0;        /* use ioVRefNum only */
  265.     }
  266.     else {
  267.         pcpy((StringPtr)tempPathname, pathname);    /* make a copy of the string and */
  268.         pb.volumeParam.ioNamePtr = (StringPtr)tempPathname;    /* use the copy so original isn't trashed */
  269.         pb.volumeParam.ioVolIndex = -1;    /* use ioNamePtr/ioVRefNum combination */
  270.     }
  271.     error = PBHGetVInfoSync(&pb);
  272.     *realVRefNum = pb.volumeParam.ioVRefNum;
  273.     return (error);
  274. }
  275.  
  276. /*****************************************************************************/
  277.  
  278. pascal    OSErr    UnmountAndEject(StringPtr pathname,
  279.                                 short vRefNum)
  280. {
  281.     HParamBlockRec pb;
  282.     Str255 tempPathname;
  283.     short driveNum;
  284.     Boolean ejected, ejectable;
  285.     DrvQElPtr drvQElem;
  286.     OSErr error;
  287.  
  288.     pb.volumeParam.ioVRefNum = vRefNum;
  289.     if (pathname == nil) {
  290.         pb.volumeParam.ioNamePtr = nil;
  291.         pb.volumeParam.ioVolIndex = 0;        /* use ioVRefNum only */
  292.     }
  293.     else {
  294.         pcpy((StringPtr)tempPathname, pathname);    /* make a copy of the string and */
  295.         pb.volumeParam.ioNamePtr = (StringPtr)tempPathname;    /* use the copy so original isn't trashed */
  296.         pb.volumeParam.ioVolIndex = -1;    /* use ioNamePtr/ioVRefNum combination */
  297.     }
  298.     error = PBHGetVInfoSync(&pb);
  299.     if (error == noErr)
  300.     {
  301.         if (pb.volumeParam.ioVDrvInfo != 0)
  302.         {
  303.             /* the volume is online and not ejected */
  304.             ejected = false;
  305.             
  306.             /* Get the drive number */
  307.             driveNum = pb.volumeParam.ioVDrvInfo;
  308.         }
  309.         else
  310.         {
  311.             /* the volume is ejected or offline */
  312.             
  313.             /* Is it ejected? */
  314.             ejected = (pb.volumeParam.ioVDRefNum > 0);
  315.             
  316.             if (ejected)
  317.             {
  318.                 /* If ejected, the drive number is ioVDRefNum */
  319.                 driveNum = pb.volumeParam.ioVDRefNum;
  320.             }
  321.             else
  322.             {
  323.                 /* If offline, the drive number is the negative of ioVDRefNum */
  324.                 driveNum = (short)-pb.volumeParam.ioVDRefNum;
  325.             }
  326.         }
  327.         
  328.         /* find the drive queue element */
  329.         drvQElem = (DrvQElPtr)(GetDrvQHdr()->qHead);
  330.         while ((drvQElem != nil) && (drvQElem->dQDrive != driveNum))
  331.             drvQElem = (DrvQElPtr)drvQElem->qLink;
  332.         
  333.         if (drvQElem != nil)
  334.         {
  335.             /* is the volume ejectable */
  336.             ejectable = (*((Ptr)((Ptr)drvQElem - 3)) != 8);
  337.         }
  338.         else
  339.         {
  340.             /* didn't find the drive!! */
  341.             ejectable = false;
  342.         }
  343.         
  344.         /* unmount the volume */
  345.         pb.volumeParam.ioNamePtr = nil;
  346.         /* ioVRefNum is already filled in from PBHGetVInfo */
  347.         error = PBUnmountVol((ParmBlkPtr)&pb);
  348.         if (error == noErr)
  349.         {
  350.             if (ejectable && !ejected)
  351.             {
  352.                 /* eject the media from the drive if needed */
  353.                 pb.volumeParam.ioVRefNum = driveNum;
  354.                 error = PBEject((ParmBlkPtr)&pb);
  355.             }
  356.         }
  357.     }
  358.     return (error);
  359. }
  360.  
  361. /*****************************************************************************/
  362.  
  363. pascal    OSErr    OnLine(FSSpecPtr volumes,
  364.                        short reqVolCount,
  365.                        short *actVolCount,
  366.                        short *volIndex)
  367. {
  368.     HParamBlockRec pb;
  369.     OSErr error = noErr;
  370.     FSSpec *endVolArray = volumes + reqVolCount;
  371.  
  372.     *actVolCount = 0;
  373.     for (; (volumes < endVolArray) && (error == noErr); ++volumes)
  374.     {
  375.         pb.volumeParam.ioNamePtr = (StringPtr) & volumes->name;
  376.         pb.volumeParam.ioVolIndex = *volIndex;
  377.         error = PBHGetVInfoSync(&pb);
  378.         if (error == noErr)
  379.         {
  380.             volumes->parID = fsRtParID;        /* the root directory's parent is 1 */
  381.             volumes->vRefNum = pb.volumeParam.ioVRefNum;
  382.             ++*volIndex;
  383.             ++*actVolCount;
  384.         }
  385.     }
  386.     return (error);
  387. }
  388.  
  389. /*****************************************************************************/
  390.  
  391. pascal    OSErr    GetDirID(short vRefNum,
  392.                          long dirID,
  393.                          StringPtr name,
  394.                          long *theDirID,
  395.                          Boolean *isDirectory)
  396. {
  397.     CInfoPBRec pb;
  398.     OSErr error;
  399.  
  400.     pb.hFileInfo.ioNamePtr = name;
  401.     pb.hFileInfo.ioVRefNum = vRefNum;
  402.     pb.hFileInfo.ioDirID = dirID;
  403.     pb.hFileInfo.ioFDirIndex = 0;    /* use ioNamePtr and ioDirID */
  404.     error = PBGetCatInfoSync(&pb);
  405.     *theDirID = pb.hFileInfo.ioDirID;
  406.     *isDirectory = (pb.hFileInfo.ioFlAttrib & 0x10) != 0;
  407.     return (error);
  408. }
  409.  
  410. /*****************************************************************************/
  411.  
  412. pascal    OSErr    DirIDFromFSSpec(const FSSpec *spec,
  413.                                 long *theDirID,
  414.                                 Boolean *isDirectory)
  415. {
  416.     return (GetDirID(spec->vRefNum, spec->parID, (StringPtr)spec->name,
  417.             theDirID, isDirectory));
  418. }
  419.  
  420. /*****************************************************************************/
  421.  
  422. pascal    OSErr    GetDirName(short vRefNum,
  423.                            long dirID,
  424.                            StringPtr name)
  425. {
  426.     CInfoPBRec pb;
  427.  
  428.     pb.hFileInfo.ioNamePtr = name;
  429.     pb.hFileInfo.ioVRefNum = vRefNum;
  430.     pb.hFileInfo.ioDirID = dirID;
  431.     pb.hFileInfo.ioFDirIndex = -1;    /* get information about ioDirID */
  432.     return (PBGetCatInfoSync(&pb));
  433. }
  434.  
  435. /*****************************************************************************/
  436.  
  437. pascal    OSErr    GetParentID(short vRefNum,
  438.                             long dirID,
  439.                             StringPtr name,
  440.                             long *parID)
  441. {
  442.     CInfoPBRec pb;
  443.     OSErr error;
  444.  
  445.     pb.hFileInfo.ioNamePtr = name;
  446.     pb.hFileInfo.ioVRefNum = vRefNum;
  447.     pb.hFileInfo.ioDirID = dirID;
  448.     pb.hFileInfo.ioFDirIndex = 0;                /* use ioNamePtr and ioDirID */
  449.     error = PBGetCatInfoSync(&pb);
  450.     *parID = pb.hFileInfo.ioFlParID;
  451.     return (error);
  452. }
  453.  
  454. /*****************************************************************************/
  455.  
  456. pascal    OSErr    GetFilenameFromPathname(const Str255 pathname,
  457.                                         Str255 filename)
  458. {
  459.     short    index;
  460.     short    nameEnd;
  461.  
  462.     /* default to no filename */
  463.     filename[0] = 0;
  464.  
  465.     /* check for no pathname */
  466.     if (pathname == nil)
  467.         return(notAFileErr);
  468.     
  469.     /* get string length */
  470.     index = pathname[0];
  471.     
  472.     /* check for empty string */
  473.     if (index == 0)
  474.         return(notAFileErr);
  475.     
  476.     /* skip over last trailing colon (if any) */
  477.     if (pathname[index] == ':')
  478.         --index;
  479.  
  480.     /* save the end of the string */
  481.     nameEnd = index;
  482.  
  483.     /* if pathname ends with multiple colons, then this pathname refers */
  484.     /* to a directory, not a file */
  485.     if (pathname[index] == ':')
  486.         return (notAFileErr);
  487.         
  488.     
  489.     /* parse backwards until we find a colon or hit the beginning of the pathname */
  490.     while ((pathname[index] != ':') && (index != 0))
  491.         --index;
  492.     
  493.     /* if we parsed to the beginning of the pathname and the pathname ended */
  494.     /* with a colon, then pathname is a full pathname to a volume, not a file */
  495.     if ((index == 0) && (pathname[pathname[0]]))
  496.         return (notAFileErr);
  497.     
  498.     /* get the filename and return noErr */
  499.     filename[0] = (char)(nameEnd - index);
  500.     BlockMove(&pathname[index+1], &filename[1], nameEnd - index);
  501.     return (noErr);
  502. }
  503.  
  504. /*****************************************************************************/
  505.  
  506. pascal    OSErr    GetObjectLocation(short vRefNum,
  507.                                   long dirID,
  508.                                   StringPtr pathname,
  509.                                   short *realVRefNum,
  510.                                   long *realParID,
  511.                                   Str255 realName,
  512.                                   Boolean *isDirectory)
  513. {
  514.     OSErr error;
  515.     long theDirID;
  516.     Str255    parPathname;
  517.     
  518.     error = DetermineVRefNum(pathname, vRefNum, realVRefNum);
  519.     if (error == noErr)
  520.     {
  521.         error = GetParentID(vRefNum, dirID, pathname, realParID);
  522.         if (error == noErr)
  523.         {
  524.             /* The object is present */
  525.             
  526.             /* Find out if the object is a file or directory and */
  527.             /* if object is a directory, get its dirID */
  528.             error = GetDirID(vRefNum, dirID, pathname, &theDirID, isDirectory);
  529.             if (error == noErr)
  530.             {
  531.                 if (*isDirectory)
  532.                 {
  533.                     /* it's a directory, get its name */
  534.                     error = GetDirName(*realVRefNum, theDirID, realName);
  535.                 }
  536.                 else
  537.                 {
  538.                     /* it's a file - parse to get the file name */
  539.                     error = GetFilenameFromPathname(pathname, realName);
  540.                 }
  541.             }
  542.         }
  543.         else if (error == fnfErr)
  544.         {
  545.             /* The object isn't present - see if its parent is present */
  546.     
  547.             /* parse to get the object name from end of pathname */
  548.             error = GetFilenameFromPathname(pathname, realName);
  549.             /* if we can't get the object name from the end, we can't continue */
  550.             if (error == noErr)
  551.             {
  552.                 /* get a copy of the pathname */
  553.                 pcpy(parPathname, pathname);
  554.                 
  555.                 /* remove the object name */
  556.                 parPathname[0] -= realName[0];
  557.                 /* and the trailing colon (if any) */
  558.                 if (pathname[pathname[0]] == ':')
  559.                     --parPathname[0];
  560.                 
  561.                 /* OK, now get the parent's directory ID */
  562.                 error = GetDirID(vRefNum, dirID, parPathname, realParID, isDirectory);
  563.                 
  564.                 *isDirectory = false;    /* we don't know what the object is really going to be */
  565.             }
  566.             if (error != noErr)
  567.                 error = dirNFErr;    /* couldn't find parent directory */
  568.             else
  569.                 error = fnfErr;        /* we found the parent, but not the file */
  570.         }
  571.     }
  572.     
  573.     /* if we get anything other than noErr or fnfErr, clear the results */
  574.     if ((error != noErr) && (error != fnfErr))
  575.     {
  576.         *realVRefNum = 0;
  577.         *realParID = 0;
  578.         realName[0] = 0;
  579.         *isDirectory = false;
  580.     }
  581.     
  582.     return (error);
  583. }
  584.  
  585. /*****************************************************************************/
  586.  
  587. pascal    OSErr    GetDirItems(short vRefNum,
  588.                             long dirID,
  589.                             StringPtr name,
  590.                             Boolean getFiles,
  591.                             Boolean getDirectories,
  592.                             FSSpecPtr items,
  593.                             short reqItemCount,
  594.                             short *actItemCount,
  595.                             short *itemIndex) /* start with 1, then use what's returned */
  596. {
  597.     CInfoPBRec pb;
  598.     OSErr error = noErr;
  599.     long theDirID;
  600.     Boolean isDirectory;
  601.     FSSpec *endItemsArray = items + reqItemCount;
  602.  
  603.     /* NOTE: If I could be sure that the caller passed a real vRefNum and real directory */
  604.     /* to this routine, I could rip out calls to DetermineVRefNum and GetDirID and this */
  605.     /* routine would be much faster because of the overhead of DetermineVRefNum and */
  606.     /* GetDirID and because GetDirID blows away the directory index hint the Macintosh */
  607.     /* file system keeps for indexed calls. I can't be sure, so for maximum throughput, */
  608.     /* pass a big array of FSSpecs so you can get the directory's contents with few calls */
  609.     /* to this routine. */
  610.     
  611.     /* get the real volume reference number */
  612.     error = DetermineVRefNum(name, vRefNum, &pb.hFileInfo.ioVRefNum);
  613.     if (error != noErr)
  614.         return (error);
  615.     
  616.     /* and the real directory ID of this directory (and make sure it IS a directory */
  617.     error = GetDirID(vRefNum, dirID, name, &theDirID, &isDirectory);
  618.     if (error != noErr)
  619.         return (error);
  620.     else if (!isDirectory)
  621.         return (dirNFErr);
  622.  
  623.  
  624.     *actItemCount = 0;
  625.     for (; (items < endItemsArray) && (error == noErr); )
  626.     {
  627.         pb.hFileInfo.ioNamePtr = (StringPtr) &items->name;
  628.         pb.hFileInfo.ioDirID = theDirID;
  629.         pb.hFileInfo.ioFDirIndex = *itemIndex;
  630.         error = PBGetCatInfoSync(&pb);
  631.         if (error == noErr)
  632.         {
  633.             items->parID = pb.hFileInfo.ioFlParID;    /* return item's parID */
  634.             items->vRefNum = pb.hFileInfo.ioVRefNum;    /* return item's vRefNum */
  635.             ++*itemIndex;    /* prepare to get next item in directory */
  636.             
  637.             if (pb.hFileInfo.ioFlAttrib & 0x10) {
  638.                 if (getDirectories) {
  639.                     ++*actItemCount; /* keep this item */
  640.                     ++items; /* point to next item */
  641.                 }
  642.             }
  643.             else {
  644.                 if (getFiles) {
  645.                     ++*actItemCount; /* keep this item */
  646.                     ++items; /* point to next item */
  647.                 }
  648.             }
  649.         }
  650.     }
  651.     return (error);
  652. }
  653.  
  654. /*****************************************************************************/
  655.  
  656. static    void    DeleteLevel(long dirToDelete,
  657.                             DeleteEnumGlobalsPtr theGlobals)
  658. {
  659.     long savedDir;
  660.     
  661.     do
  662.     {
  663.         /* prepare to delete directory */
  664.         theGlobals->myPB.ciPB.dirInfo.ioNamePtr = (StringPtr)&theGlobals->itemName;
  665.         theGlobals->myPB.ciPB.dirInfo.ioFDirIndex = 1;    /* get first item */
  666.         theGlobals->myPB.ciPB.dirInfo.ioDrDirID = dirToDelete;    /* in this directory */
  667.         theGlobals->error = PBGetCatInfoSync(&(theGlobals->myPB.ciPB));
  668.         if (theGlobals->error == noErr)
  669.         {
  670.             savedDir = dirToDelete;
  671.             /* We have an item.  Is it a file or directory? */
  672.             if ((theGlobals->myPB.ciPB.dirInfo.ioFlAttrib & 0x10) != 0)
  673.             {
  674.                 /* it's a directory */
  675.                 savedDir = theGlobals->myPB.ciPB.dirInfo.ioDrDirID;    /* save dirID of directory instead */
  676.                 DeleteLevel(theGlobals->myPB.ciPB.dirInfo.ioDrDirID, theGlobals);    /* Delete its contents */
  677.                 theGlobals->myPB.ciPB.dirInfo.ioNamePtr = nil;    /* prepare to delete directory */
  678.             }
  679.             if (theGlobals->error == noErr)
  680.             {
  681.                 theGlobals->myPB.ciPB.dirInfo.ioDrDirID = savedDir;    /* restore dirID */
  682.                 theGlobals->error = PBHDeleteSync(&(theGlobals->myPB.hPB));    /* delete this item */
  683.                 if (theGlobals->error == fLckdErr)
  684.                 {
  685.                     (void) PBHRstFLockSync(&(theGlobals->myPB.hPB));    /* unlock it */
  686.                     theGlobals->error = PBHDeleteSync(&(theGlobals->myPB.hPB));    /* and try again */
  687.                 }
  688.             }
  689.         }
  690.     }
  691.     while (theGlobals->error == noErr);
  692.     if (theGlobals->error == fnfErr)
  693.         theGlobals->error = noErr;
  694. }
  695.  
  696. /*****************************************************************************/
  697.  
  698. pascal    OSErr    DeleteDirectoryContents(short vRefNum,
  699.                                          long dirID,
  700.                                         StringPtr name)
  701. {
  702.     DeleteEnumGlobals theGlobals;
  703.     Boolean    isDirectory;
  704.     OSErr error;
  705.  
  706.     /*  Get the real dirID and make sure it is a directory. */
  707.     error = GetDirID(vRefNum, dirID, name, &dirID, &isDirectory);
  708.     if (error != noErr)
  709.         return (error);
  710.     if (!isDirectory)
  711.         return (dirNFErr);
  712.     
  713.     /* Get the real vRefNum */
  714.     error = DetermineVRefNum(name, vRefNum, &vRefNum);
  715.     if (error != noErr)
  716.         return (error);
  717.     
  718.     /* Set up the globals we need to access from the recursive routine. */
  719.     theGlobals.myPB.ciPB.dirInfo.ioVRefNum = vRefNum;
  720.         
  721.     /* Here we go into recursion land... */
  722.     DeleteLevel(dirID, &theGlobals);
  723.     return (theGlobals.error);
  724. }
  725.  
  726. /*****************************************************************************/
  727.  
  728. pascal    OSErr    DeleteDirectory(short vRefNum,
  729.                                 long dirID,
  730.                                 StringPtr name)
  731. {
  732.     OSErr error;
  733.     
  734.     /* Make sure a directory was specified and then delete its contents */
  735.     error = DeleteDirectoryContents(vRefNum, dirID, name);
  736.     if (error == noErr)
  737.     {
  738.         error = HDelete(vRefNum, dirID, name);
  739.         if (error == fLckdErr)
  740.         {
  741.             (void) HRstFLock(vRefNum, dirID, name);    /* unlock the directory locked by AppleShare */
  742.             error = HDelete(vRefNum, dirID, name);    /* and try again */
  743.         }
  744.     }
  745.     return (error);
  746. }
  747.  
  748. /*****************************************************************************/
  749.  
  750. pascal    OSErr    BumpDate(short vRefNum,
  751.                          long dirID,
  752.                          StringPtr name)
  753. /* Given a file or directory, change its modification date to the current date/time. */
  754. {
  755.     CInfoPBRec pb;
  756.     OSErr error;
  757.     unsigned long secs;
  758.  
  759.     pb.hFileInfo.ioNamePtr = name;
  760.     pb.hFileInfo.ioVRefNum = vRefNum;
  761.     pb.hFileInfo.ioDirID = dirID;
  762.     pb.hFileInfo.ioFDirIndex = 0;    /* use ioNamePtr and ioDirID */
  763.     error = PBGetCatInfoSync(&pb);
  764.     if (error == noErr)
  765.     {
  766.         GetDateTime(&secs);
  767.         /* set mod date to current date, or one second into the future
  768.             if mod date = current date */
  769.         pb.hFileInfo.ioFlMdDat = (secs == pb.hFileInfo.ioFlMdDat) ? (++secs) : (secs);
  770.         pb.hFileInfo.ioDirID = dirID;
  771.         error = PBSetCatInfoSync(&pb);
  772.     }
  773.     return (error);
  774. }
  775.  
  776. /*****************************************************************************/
  777.  
  778. pascal    OSErr    FSpBumpDate(const FSSpec *spec)
  779. {
  780.     return (BumpDate(spec->vRefNum, spec->parID, (StringPtr)spec->name));
  781. }
  782.  
  783. /*****************************************************************************/
  784.  
  785. pascal    OSErr    ChangeCreatorType(short vRefNum,
  786.                                   long dirID,
  787.                                   const Str255 name,
  788.                                   OSType creator,
  789.                                   OSType fileType)
  790. {
  791.     CInfoPBRec pb;
  792.     OSErr error;
  793.     long parID;
  794.  
  795.     pb.hFileInfo.ioNamePtr = (StringPtr)name;
  796.     pb.hFileInfo.ioVRefNum = vRefNum;
  797.     pb.hFileInfo.ioDirID = dirID;
  798.     pb.hFileInfo.ioFDirIndex = 0;    /* use ioNamePtr and ioDirID */
  799.     error = PBGetCatInfoSync(&pb);
  800.     if (error == noErr)
  801.     {
  802.         if (pb.hFileInfo.ioFlAttrib & 0x10)    /* if directory */
  803.             return (notAFileErr);            /* do nothing and return error */
  804.             
  805.         parID = pb.hFileInfo.ioFlParID;    /* save parent dirID for BumpDate call */
  806.  
  807.         /* If creator not 0x00000000, change creator */
  808.         if (creator != (OSType)0x00000000)
  809.             pb.hFileInfo.ioFlFndrInfo.fdCreator = creator;
  810.         
  811.         /* If fileType not 0x00000000, change fileType */
  812.         if (fileType != (OSType)0x00000000)
  813.             pb.hFileInfo.ioFlFndrInfo.fdType = fileType;
  814.             
  815.         pb.hFileInfo.ioDirID = dirID;
  816.         error = PBSetCatInfoSync(&pb);    /* now, save the new information back to disk */
  817.  
  818.         if ((error == noErr) && (parID != fsRtParID)) /* can't bump fsRtParID */
  819.             error = BumpDate(vRefNum, parID, nil);
  820.                 /* and bump the parent directory's mod date to wake up the Finder */
  821.                 /* to the change we just made */
  822.     }
  823.     return (error);
  824. }
  825.  
  826. /*****************************************************************************/
  827.  
  828. pascal    OSErr    FSpChangeCreatorType(const FSSpec *spec,
  829.                                      OSType creator,
  830.                                      OSType fileType)
  831. {
  832.     return (ChangeCreatorType(spec->vRefNum, spec->parID, spec->name, creator, fileType));
  833. }
  834.  
  835. /*****************************************************************************/
  836.  
  837. pascal    OSErr    ChangeFDFlags(short vRefNum,
  838.                               long dirID,
  839.                               StringPtr name,
  840.                               Boolean    setBits,
  841.                               unsigned short flagBits)
  842. {
  843.     CInfoPBRec pb;
  844.     OSErr error;
  845.     long parID;
  846.  
  847.     pb.hFileInfo.ioNamePtr = name;
  848.     pb.hFileInfo.ioVRefNum = vRefNum;
  849.     pb.hFileInfo.ioDirID = dirID;
  850.     pb.hFileInfo.ioFDirIndex = 0;    /* use ioNamePtr and ioDirID */
  851.     error = PBGetCatInfoSync(&pb);
  852.     if (error == noErr)
  853.     {
  854.         parID = pb.hFileInfo.ioFlParID;    /* save parent dirID for BumpDate call */
  855.  
  856.         pb.hFileInfo.ioFlFndrInfo.fdFlags = (setBits) ? 
  857.             (pb.hFileInfo.ioFlFndrInfo.fdFlags | flagBits) : /* OR in the bits */
  858.             (pb.hFileInfo.ioFlFndrInfo.fdFlags & (0xffff ^ flagBits)); /* AND out the bits */
  859.                 /* set or clear the appropriate bits in the Finder flags */
  860.             
  861.         pb.hFileInfo.ioDirID = dirID;
  862.         error = PBSetCatInfoSync(&pb);    /* now, save the new information back to disk */
  863.  
  864.         if ((error == noErr) && (parID != fsRtParID)) /* can't bump fsRtParID */
  865.             error = BumpDate(vRefNum, parID, nil);
  866.                 /* and bump the parent directory's mod date to wake up the Finder */
  867.                 /* to the change we just made */
  868.     }
  869.     return (error);
  870. }
  871.  
  872. /*****************************************************************************/
  873.  
  874. pascal    OSErr    FSpChangeFDFlags(const FSSpec *spec,
  875.                                  Boolean setBits,
  876.                                  unsigned short flagBits)
  877. {
  878.     return (ChangeFDFlags(spec->vRefNum, spec->parID, (StringPtr)spec->name, setBits, flagBits));
  879. }
  880.  
  881. /*****************************************************************************/
  882.  
  883. pascal    OSErr    SetIsInvisible(short vRefNum,
  884.                                long dirID,
  885.                                StringPtr name)
  886.     /* Given a file or directory, make it invisible. */
  887. {
  888.     return (ChangeFDFlags(vRefNum, dirID, name, true, 0x4000));
  889. }
  890.  
  891. /*****************************************************************************/
  892.  
  893. pascal    OSErr    FSpSetIsInvisible(const FSSpec *spec)
  894.     /* Given a file or directory, make it invisible. */
  895. {
  896.     return (ChangeFDFlags(spec->vRefNum, spec->parID, (StringPtr)spec->name, true, 0x4000));
  897. }
  898.  
  899. /*****************************************************************************/
  900.  
  901. pascal    OSErr    ClearIsInvisible(short vRefNum,
  902.                                  long dirID,
  903.                                  StringPtr name)
  904.     /* Given a file or directory, make it visible. */
  905. {
  906.     return (ChangeFDFlags(vRefNum, dirID, name, false, 0x4000));
  907. }
  908.  
  909. /*****************************************************************************/
  910.  
  911. pascal    OSErr    FSpClearIsInvisible(const FSSpec *spec)
  912.     /* Given a file or directory, make it visible. */
  913. {
  914.     return (ChangeFDFlags(spec->vRefNum, spec->parID, (StringPtr)spec->name, false, 0x4000));
  915. }
  916.  
  917. /*****************************************************************************/
  918.  
  919. pascal    OSErr    SetNameLocked(short vRefNum,
  920.                               long dirID,
  921.                               StringPtr name)
  922.     /* Given a file or directory, lock its name. */
  923. {
  924.     return (ChangeFDFlags(vRefNum, dirID, name, true, 0x1000));
  925. }
  926.  
  927. /*****************************************************************************/
  928.  
  929. pascal    OSErr    FSpSetNameLocked(const FSSpec *spec)
  930.     /* Given a file or directory, lock its name. */
  931. {
  932.     return (ChangeFDFlags(spec->vRefNum, spec->parID, (StringPtr)spec->name, true, 0x1000));
  933. }
  934.  
  935. /*****************************************************************************/
  936.  
  937. pascal    OSErr    ClearNameLocked(short vRefNum,
  938.                                 long dirID,
  939.                                 StringPtr name)
  940.     /* Given a file or directory, unlock its name. */
  941. {
  942.     return (ChangeFDFlags(vRefNum, dirID, name, false, 0x1000));
  943. }
  944.  
  945. /*****************************************************************************/
  946.  
  947. pascal    OSErr    FSpClearNameLocked(const FSSpec *spec)
  948.     /* Given a file or directory, unlock its name. */
  949. {
  950.     return (ChangeFDFlags(spec->vRefNum, spec->parID, (StringPtr)spec->name, false, 0x1000));
  951. }
  952.  
  953. /*****************************************************************************/
  954.  
  955. pascal    OSErr    SetIsStationery(short vRefNum,
  956.                                 long dirID,
  957.                                 const Str255 name)
  958.     /* Given a file, make it a stationery pad. */
  959. {
  960.     return (ChangeFDFlags(vRefNum, dirID, (StringPtr)name, true, 0x0800));
  961. }
  962.  
  963. /*****************************************************************************/
  964.  
  965. pascal    OSErr    FSpSetIsStationery(const FSSpec *spec)
  966.     /* Given a file, make it a stationery pad. */
  967. {
  968.     return (ChangeFDFlags(spec->vRefNum, spec->parID, (StringPtr)spec->name, true, 0x0800));
  969. }
  970.  
  971. /*****************************************************************************/
  972.  
  973. pascal    OSErr    ClearIsStationery(short vRefNum,
  974.                                   long dirID,
  975.                                   const Str255 name)
  976.     /* Given a file, clear the stationery bit. */
  977. {
  978.     return (ChangeFDFlags(vRefNum, dirID, (StringPtr)name, false, 0x0800));
  979. }
  980.  
  981. /*****************************************************************************/
  982.  
  983. pascal    OSErr    FSpClearIsStationery(const FSSpec *spec)
  984.     /* Given a file, clear the stationery bit. */
  985. {
  986.     return (ChangeFDFlags(spec->vRefNum, spec->parID, (StringPtr)spec->name, false, 0x0800));
  987. }
  988.  
  989. /*****************************************************************************/
  990.  
  991. pascal    OSErr    SetHasCustomIcon(short vRefNum,
  992.                                  long dirID,
  993.                                  StringPtr name)
  994.     /* Given a file or directory, indicate that it has a custom icon. */
  995. {
  996.     return (ChangeFDFlags(vRefNum, dirID, name, true, 0x0400));
  997. }
  998.  
  999. /*****************************************************************************/
  1000.  
  1001. pascal    OSErr    FSpSetHasCustomIcon(const FSSpec *spec)
  1002.     /* Given a file or directory, indicate that it has a custom icon. */
  1003. {
  1004.     return (ChangeFDFlags(spec->vRefNum, spec->parID, (StringPtr)spec->name, true, 0x0400));
  1005. }
  1006.  
  1007. /*****************************************************************************/
  1008.  
  1009. pascal    OSErr    ClearHasCustomIcon(short vRefNum,
  1010.                                    long dirID,
  1011.                                    StringPtr name)
  1012.     /* Given a file or directory, indicate that it does not have a custom icon. */
  1013. {
  1014.     return (ChangeFDFlags(vRefNum, dirID, name, false, 0x0400));
  1015. }
  1016.  
  1017. /*****************************************************************************/
  1018.  
  1019. pascal    OSErr    FSpClearHasCustomIcon(const FSSpec *spec)
  1020.     /* Given a file or directory, indicate that it does not have a custom icon. */
  1021. {
  1022.     return (ChangeFDFlags(spec->vRefNum, spec->parID, (StringPtr)spec->name, false, 0x0400));
  1023. }
  1024.  
  1025. /*****************************************************************************/
  1026.  
  1027. pascal    OSErr    ClearHasBeenInited(short vRefNum,
  1028.                                    long dirID,
  1029.                                    StringPtr name)
  1030.     /* Given a file, clear its "has been inited" bit. */
  1031. {
  1032.     return (ChangeFDFlags(vRefNum, dirID, (StringPtr)name, false, 0x0100));
  1033. }
  1034.  
  1035. /*****************************************************************************/
  1036.  
  1037. pascal    OSErr    FSpClearHasBeenInited(const FSSpec *spec)
  1038.     /* Given a file, clear its "has been inited" bit. */
  1039. {
  1040.     return (ChangeFDFlags(spec->vRefNum, spec->parID, (StringPtr)spec->name, false, 0x0100));
  1041. }
  1042.  
  1043. /*****************************************************************************/
  1044.  
  1045. pascal    OSErr    CopyFileMgrAttributes(short srcVRefNum,
  1046.                                       long srcDirID,
  1047.                                       StringPtr srcName,
  1048.                                       short dstVRefNum,
  1049.                                       long dstDirID,
  1050.                                       StringPtr dstName,
  1051.                                       Boolean copyLockBit)
  1052. {
  1053.     UniversalFMPB pb;
  1054.     OSErr error;
  1055.     Boolean objectIsDirectory;
  1056.  
  1057.     pb.ciPB.hFileInfo.ioVRefNum = srcVRefNum;
  1058.     pb.ciPB.hFileInfo.ioDirID = srcDirID;
  1059.     pb.ciPB.hFileInfo.ioNamePtr = srcName;
  1060.     pb.ciPB.hFileInfo.ioFDirIndex = 0;
  1061.     error = PBGetCatInfoSync(&pb.ciPB);
  1062.     if (error == noErr)
  1063.     {
  1064.         objectIsDirectory = (pb.ciPB.hFileInfo.ioFlAttrib & 0x10);
  1065.         pb.ciPB.hFileInfo.ioVRefNum = dstVRefNum;
  1066.         pb.ciPB.hFileInfo.ioDirID = dstDirID;
  1067.         pb.ciPB.hFileInfo.ioNamePtr = dstName;
  1068.         /* don't copy the hasBeenInited bit */
  1069.         pb.ciPB.hFileInfo.ioFlFndrInfo.fdFlags = (pb.ciPB.hFileInfo.ioFlFndrInfo.fdFlags & 0xfeff);
  1070.         error = PBSetCatInfoSync(&pb.ciPB);
  1071.         if ((error == noErr) && (copyLockBit) && (pb.ciPB.hFileInfo.ioFlAttrib & 0x01))
  1072.         {
  1073.             error = PBHSetFLockSync(&pb.hPB);
  1074.             if ((error != noErr) && (objectIsDirectory))
  1075.                 error = noErr; /* ignore lock errors if destination is directory */
  1076.         }
  1077.     }
  1078.     return (error);
  1079. }
  1080.  
  1081. /*****************************************************************************/
  1082.  
  1083. pascal    OSErr    FSpCopyFileMgrAttributes(const FSSpec *srcSpec,
  1084.                                          const FSSpec *dstSpec,
  1085.                                          Boolean copyLockBit)
  1086. {
  1087.     return (CopyFileMgrAttributes(srcSpec->vRefNum, srcSpec->parID, (StringPtr)srcSpec->name,
  1088.                                   dstSpec->vRefNum, dstSpec->parID, (StringPtr)dstSpec->name,
  1089.                                   copyLockBit));
  1090. }
  1091.  
  1092. /*****************************************************************************/
  1093.  
  1094. pascal    OSErr    HOpenAware(short vRefNum,
  1095.                            long dirID,
  1096.                            const Str255 fileName,
  1097.                            short denyModes,
  1098.                            short *refNum)
  1099. {
  1100.     HParamBlockRec pb;
  1101.     OSErr error;
  1102.     GetVolParmsInfoBuffer volParmsInfo;
  1103.     long infoSize = sizeof(GetVolParmsInfoBuffer);
  1104.  
  1105.     pb.ioParam.ioVersNum = 0;
  1106.     pb.ioParam.ioMisc = nil;
  1107.     pb.fileParam.ioNamePtr = (StringPtr)fileName;
  1108.     pb.fileParam.ioVRefNum = vRefNum;
  1109.     pb.fileParam.ioDirID = dirID;
  1110.  
  1111.     /* get volume attributes */
  1112.     /* this preflighting is needed because Foreign File Access based file systems don't */
  1113.     /* return the correct error result to the OpenDeny call */
  1114.     error = HGetVolParms((StringPtr)fileName, vRefNum, &volParmsInfo, &infoSize);
  1115.     if (error == noErr)
  1116.     {
  1117.         /* if volume supports OpenDeny, use it and return */
  1118.         if (hasOpenDeny(volParmsInfo))
  1119.         {
  1120.             pb.accessParam.ioDenyModes = denyModes;
  1121.             error = PBHOpenDenySync(&pb);
  1122.             *refNum = pb.ioParam.ioRefNum;
  1123.             return (error);
  1124.         }
  1125.     }
  1126.     else if (error != paramErr)    /* paramErr is OK, it just means this volume doesn't support GetVolParms */
  1127.         return (error);
  1128.     
  1129.     /* OpenDeny isn't supported, so try File Manager Open functions */
  1130.     /* Set File Manager permissions to closest thing possible */
  1131.     pb.ioParam.ioPermssn = ((denyModes == dmWr) || (denyModes == dmRdWr)) ? (fsRdWrShPerm) : (denyModes % 4);
  1132.  
  1133.     error = PBHOpenDFSync(&pb);                /* Try OpenDF */
  1134.     if (error == paramErr)
  1135.         error = PBHOpenSync(&pb);            /* OpenDF not supported, so try Open */
  1136.     *refNum = pb.ioParam.ioRefNum;
  1137.     return (error);
  1138. }
  1139.  
  1140. /*****************************************************************************/
  1141.  
  1142. pascal    OSErr    FSpOpenAware(const FSSpec *spec,
  1143.                              short denyModes,
  1144.                              short *refNum)
  1145. {
  1146.     return (HOpenAware(spec->vRefNum, spec->parID, spec->name, denyModes, refNum));
  1147. }
  1148.  
  1149. /*****************************************************************************/
  1150.  
  1151. pascal    OSErr    HOpenRFAware(short vRefNum,
  1152.                              long dirID,
  1153.                              const Str255 fileName,
  1154.                              short denyModes,
  1155.                              short *refNum)
  1156. {
  1157.     HParamBlockRec pb;
  1158.     OSErr error;
  1159.     GetVolParmsInfoBuffer volParmsInfo;
  1160.     long infoSize = sizeof(GetVolParmsInfoBuffer);
  1161.  
  1162.     pb.ioParam.ioVersNum = 0;
  1163.     pb.ioParam.ioMisc = nil;
  1164.     pb.fileParam.ioNamePtr = (StringPtr)fileName;
  1165.     pb.fileParam.ioVRefNum = vRefNum;
  1166.     pb.fileParam.ioDirID = dirID;
  1167.  
  1168.     /* get volume attributes */
  1169.     /* this preflighting is needed because Foreign File Access based file systems don't */
  1170.     /* return the correct error result to the OpenRFDeny call */
  1171.     error = HGetVolParms((StringPtr)fileName, vRefNum, &volParmsInfo, &infoSize);
  1172.     if (error == noErr)
  1173.     {
  1174.         /* if volume supports OpenRFDeny, use it and return */
  1175.         if (hasOpenDeny(volParmsInfo))
  1176.         {
  1177.             pb.accessParam.ioDenyModes = denyModes;
  1178.             error = PBHOpenRFDenySync(&pb);
  1179.             *refNum = pb.ioParam.ioRefNum;
  1180.             return (error);
  1181.         }
  1182.     }
  1183.     else if (error != paramErr)    /* paramErr is OK, it just means this volume doesn't support GetVolParms */
  1184.         return (error);
  1185.  
  1186.     /* OpenRFDeny isn't supported, so try File Manager OpenRF function */
  1187.     /* Set File Manager permissions to closest thing possible */
  1188.     pb.ioParam.ioPermssn = ((denyModes == dmWr) || (denyModes == dmRdWr)) ? (fsRdWrShPerm) : (denyModes % 4);
  1189.  
  1190.     error = PBHOpenRFSync(&pb);
  1191.     *refNum = pb.ioParam.ioRefNum;
  1192.     return (error);
  1193. }
  1194.  
  1195. /*****************************************************************************/
  1196.  
  1197. pascal    OSErr    FSpOpenRFAware(const FSSpec *spec,
  1198.                                short denyModes,
  1199.                                short *refNum)
  1200. {
  1201.     return (HOpenRFAware(spec->vRefNum, spec->parID, spec->name, denyModes, refNum));
  1202. }
  1203.  
  1204. /*****************************************************************************/
  1205.  
  1206. pascal    OSErr    FSReadNoCache(short refNum,
  1207.                               long *count,
  1208.                               void *buffPtr)
  1209. {
  1210.     ParamBlockRec pb;
  1211.     OSErr error;
  1212.  
  1213.     pb.ioParam.ioRefNum = refNum;
  1214.     pb.ioParam.ioBuffer = (Ptr)buffPtr;
  1215.     pb.ioParam.ioReqCount = *count;
  1216.     pb.ioParam.ioPosMode = fsAtMark + 0x0020;    /* fsAtMark + noCacheBit */
  1217.     pb.ioParam.ioPosOffset = 0;
  1218.     error = PBReadSync(&pb);
  1219.     *count = pb.ioParam.ioActCount;
  1220.     return (error);
  1221. }
  1222.  
  1223. /*****************************************************************************/
  1224.  
  1225. pascal    OSErr    FSWriteNoCache(short refNum,
  1226.                                long *count,
  1227.                                const void *buffPtr)
  1228. {
  1229.     ParamBlockRec pb;
  1230.     OSErr error;
  1231.  
  1232.     pb.ioParam.ioRefNum = refNum;
  1233.     pb.ioParam.ioBuffer = (Ptr)buffPtr;
  1234.     pb.ioParam.ioReqCount = *count;
  1235.     pb.ioParam.ioPosMode = fsAtMark + 0x0020;    /* fsAtMark + noCacheBit */
  1236.     pb.ioParam.ioPosOffset = 0;
  1237.     error = PBWriteSync(&pb);
  1238.     *count = pb.ioParam.ioActCount;
  1239.     return (error);
  1240. }
  1241.  
  1242. /*****************************************************************************/
  1243.  
  1244. pascal    OSErr    CopyFork(short srcRefNum,
  1245.                          short dstRefNum,
  1246.                          void *copyBufferPtr,
  1247.                          long copyBufferSize)
  1248. {
  1249.     ParamBlockRec srcPB;
  1250.     ParamBlockRec dstPB;
  1251.     OSErr srcError;
  1252.     OSErr dstError;
  1253.  
  1254.     if ((copyBufferPtr == nil) || (copyBufferSize == 0))
  1255.         return (paramErr);
  1256.     
  1257.     srcPB.ioParam.ioRefNum = srcRefNum;
  1258.     dstPB.ioParam.ioRefNum = dstRefNum;
  1259.  
  1260.     /* preallocate the destination fork and */
  1261.     /* ensure the destination fork's EOF is correct after the copy */
  1262.     srcError = PBGetEOFSync(&srcPB);
  1263.     if (srcError != noErr)
  1264.         return (srcError);
  1265.     dstPB.ioParam.ioMisc = srcPB.ioParam.ioMisc;
  1266.     dstError = PBSetEOFSync(&dstPB);
  1267.     if (dstError != noErr)
  1268.         return (dstError);
  1269.  
  1270.     /* reset source fork's mark */
  1271.     srcPB.ioParam.ioPosMode = fsFromStart;
  1272.     srcPB.ioParam.ioPosOffset = 0;
  1273.     srcError = PBSetFPosSync(&srcPB);
  1274.     if (srcError != noErr)
  1275.         return (srcError);
  1276.  
  1277.     /* reset destination fork's mark */
  1278.     dstPB.ioParam.ioPosMode = fsFromStart;
  1279.     dstPB.ioParam.ioPosOffset = 0;
  1280.     dstError = PBSetFPosSync(&srcPB);
  1281.     if (dstError != noErr)
  1282.         return (dstError);
  1283.  
  1284.     /* set up fields that won't change in the loop */
  1285.     srcPB.ioParam.ioBuffer = (Ptr)copyBufferPtr;
  1286.     srcPB.ioParam.ioPosMode = fsAtMark + 0x0020;/* fsAtMark + noCacheBit */
  1287.     srcPB.ioParam.ioReqCount = ((copyBufferSize >= 512) && (copyBufferSize % 512)) ? (copyBufferSize / 512) * 512 : (copyBufferSize);
  1288.     /* If copyBufferSize is greater than 512 bytes, make it a multiple of 512 bytes */
  1289.     /* This will make writes on local volumes faster */
  1290.  
  1291.     dstPB.ioParam.ioBuffer = (Ptr)copyBufferPtr;
  1292.     dstPB.ioParam.ioPosMode = fsAtMark + 0x0020;/* fsAtMark + noCacheBit */
  1293.  
  1294.     while ((srcError == noErr) && (dstError == noErr))
  1295.     {
  1296.         srcError = PBReadSync(&srcPB);
  1297.         dstPB.ioParam.ioReqCount = srcPB.ioParam.ioActCount;
  1298.         dstError = PBWriteSync(&dstPB);
  1299.     }
  1300.  
  1301.     /* make sure there were no errors at the destination */
  1302.     if (dstError != noErr)
  1303.         return (dstError);
  1304.  
  1305.     /* make sure the only error at the source was eofErr */
  1306.     if (srcError != eofErr)
  1307.         return (srcError);
  1308.  
  1309.     return (noErr);
  1310. }
  1311.  
  1312. /*****************************************************************************/
  1313.  
  1314. pascal    OSErr    GetFileLocation(short refNum,
  1315.                                 short *vRefNum,
  1316.                                 long *dirID,
  1317.                                 StringPtr fileName)
  1318. {
  1319.     FCBPBRec pb;
  1320.     OSErr error;
  1321.  
  1322.     pb.ioNamePtr = fileName;
  1323.     pb.ioVRefNum = 0;
  1324.     pb.ioRefNum = refNum;
  1325.     pb.ioFCBIndx = 0;
  1326.     error = PBGetFCBInfoSync(&pb);
  1327.     *vRefNum = pb.ioFCBVRefNum;
  1328.     *dirID = pb.ioFCBParID;
  1329.     return (error);
  1330. }
  1331.  
  1332. /*****************************************************************************/
  1333.  
  1334. pascal    OSErr    FSpGetFileLocation(short refNum,
  1335.                                    FSSpec *spec)
  1336. {
  1337.     return (GetFileLocation(refNum, &(spec->vRefNum), &(spec->parID), spec->name));
  1338. }
  1339.  
  1340. /*****************************************************************************/
  1341.  
  1342. pascal    OSErr    CopyDirectoryAccess(short srcVRefNum,
  1343.                                     long srcDirID,
  1344.                                     StringPtr srcName,
  1345.                                     short dstVRefNum,
  1346.                                     long dstDirID,
  1347.                                     StringPtr dstName)
  1348. {    
  1349.     OSErr err;
  1350.     GetVolParmsInfoBuffer infoBuffer;    /* Where PBGetVolParms dumps its info */
  1351.     long    dstServerAdr;                /* AppleTalk server address of destination (if any) */
  1352.     Boolean    dstHasBlankAccessPrivileges;
  1353.     long    ownerID, groupID, accessRights;
  1354.     long    tempLong;
  1355.  
  1356.     /* See if destination supports directory access control */
  1357.     tempLong = sizeof(infoBuffer);
  1358.     err = HGetVolParms(dstName, dstVRefNum, &infoBuffer, &tempLong);
  1359.     if ((err != noErr) && (err != paramErr))
  1360.         return (err);
  1361.     if ((err != paramErr) && hasAccessCntl(infoBuffer)) {
  1362.         dstServerAdr = infoBuffer.vMServerAdr;
  1363.         dstHasBlankAccessPrivileges = hasBlankAccessPrivileges(infoBuffer);
  1364.     }
  1365.     else
  1366.         /* If destination doesn't support access privileges, */
  1367.         /* then there's nothing left to do here */
  1368.         return (noErr);
  1369.  
  1370.     /* We may have to do something with access privileges at the destination. */
  1371.     
  1372.     accessRights = 0;    /* clear so we can tell if anything was done with this */
  1373.  
  1374.     /* See if source supports directory access control and is on same server */
  1375.     tempLong = sizeof(infoBuffer);
  1376.     err = HGetVolParms(srcName, srcVRefNum, &infoBuffer, &tempLong);
  1377.     if ((err != noErr) && (err != paramErr))
  1378.         return (err);
  1379.     if ((err != paramErr) && hasAccessCntl(infoBuffer)) {
  1380.         /* Make sure both locations are on the same file server */
  1381.         if (dstServerAdr == infoBuffer.vMServerAdr) {
  1382.             /* copy'm */
  1383.             err = HGetDirAccess(srcVRefNum, srcDirID, srcName, &ownerID, &groupID, &accessRights);
  1384.             if (err == noErr)
  1385.                 err = HSetDirAccess(dstVRefNum, dstDirID, dstName, ownerID, groupID, accessRights);
  1386.         }
  1387.     }
  1388.     return (err);
  1389. }
  1390.  
  1391. /*****************************************************************************/
  1392.  
  1393. pascal    OSErr    FSpCopyDirectoryAccess(const FSSpec *srcSpec,
  1394.                                        const FSSpec *dstSpec)
  1395. {
  1396.     return (CopyDirectoryAccess(srcSpec->vRefNum, srcSpec->parID, (StringPtr)srcSpec->name,
  1397.                                 dstSpec->vRefNum, dstSpec->parID, (StringPtr)dstSpec->name));
  1398. }
  1399.  
  1400. /*****************************************************************************/
  1401.  
  1402. pascal    OSErr    HMoveRenameCompat(short vRefNum,
  1403.                                   long srcDirID,
  1404.                                   const Str255 srcName,
  1405.                                   long dstDirID,
  1406.                                   StringPtr dstpathName,
  1407.                                   StringPtr copyName)
  1408. {
  1409.     OSErr                    error;
  1410.     GetVolParmsInfoBuffer    volParmsInfo;
  1411.     long                    infoSize = sizeof(GetVolParmsInfoBuffer);
  1412.     short                    realVRefNum;
  1413.     long                    realParID;
  1414.     Str255                    realName;
  1415.     short                    tempItemsVRefNum;
  1416.     long                    tempItemsDirID;
  1417.     Boolean                    isDirectory;
  1418.     
  1419.     /* get volume attributes */
  1420.     error = HGetVolParms((StringPtr)srcName, vRefNum, &volParmsInfo, &infoSize);
  1421.     if (error == noErr)
  1422.     {
  1423.         /* if volume supports move and rename, use it and return */
  1424.         if (hasMoveRename(volParmsInfo))
  1425.             return (HMoveRename(vRefNum, srcDirID, srcName, dstDirID, dstpathName, copyName));
  1426.     }
  1427.     else if (error != paramErr)    /* paramErr is OK, it just means this volume doesn't support GetVolParms */
  1428.         return (error);
  1429.     
  1430.     /* MoveRename isn't supported by this volume, so do it by hand */
  1431.     
  1432.     /* if copyName isn't supplied, we can simply CatMove and return */
  1433.     if (copyName == nil)
  1434.         return (CatMove(vRefNum, srcDirID, srcName, dstDirID, dstpathName));
  1435.     
  1436.     /* renaming is required, so we have some work to do... */
  1437.     
  1438.     /* get the object's real name */
  1439.     error = GetObjectLocation(vRefNum, srcDirID, (StringPtr)srcName,
  1440.                                 &realVRefNum, &realParID, realName, &isDirectory);
  1441.     if (error != noErr)
  1442.         return (error);
  1443.     
  1444.     /* find temporary items folder */
  1445.     error = FindFolder(realVRefNum, kTemporaryFolderType, kCreateFolder,
  1446.                         &tempItemsVRefNum, &tempItemsDirID);
  1447.     if (error != noErr)
  1448.         return (error);
  1449.     
  1450.     /* move the object to a temporary items folder for renaming */
  1451.     error = CatMove(realVRefNum, realParID, realName, tempItemsDirID, nil);
  1452.     if (error != noErr)
  1453.         return (error);
  1454.     
  1455.     /* rename the object */    
  1456.     error = HRename(tempItemsVRefNum, tempItemsDirID, realName, copyName);
  1457.     if (error == noErr)
  1458.     {
  1459.         /* move object to its new home */
  1460.         error = CatMove(tempItemsVRefNum, tempItemsDirID, copyName, dstDirID, dstpathName);
  1461.         if (error == noErr)
  1462.             return (error);    /* IF NO ERRORS EXIT HERE! */
  1463.         
  1464.         /* Error handling: rename object back to original name - keep real error */
  1465.         (void) HRename(tempItemsVRefNum, tempItemsDirID, copyName, realName);
  1466.     }
  1467.     
  1468.     /* Error handling: move object back to original location - keep real error */
  1469.     (void) CatMove(tempItemsVRefNum, tempItemsDirID, realName, realParID, nil);
  1470.     
  1471.     return (error);
  1472. }
  1473.  
  1474. /*****************************************************************************/
  1475.  
  1476. pascal    OSErr    FSpMoveRenameCompat(const FSSpec *srcSpec,
  1477.                                     const FSSpec *dstSpec,
  1478.                                     StringPtr copyName)
  1479. {
  1480.     /* make sure the FSSpecs refer to the same volume */
  1481.     if (srcSpec->vRefNum != dstSpec->vRefNum)
  1482.         return (diffVolErr);
  1483.     return (HMoveRenameCompat(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
  1484.                       dstSpec->parID, (StringPtr)dstSpec->name, copyName));
  1485. }
  1486.  
  1487. /*****************************************************************************/
  1488.  
  1489. pascal    void    BuildAFPVolMountInfo(short theFlags,
  1490.                                      char theNBPInterval,
  1491.                                      char theNBPCount,
  1492.                                      short theUAMType,
  1493.                                      Str31 theZoneName,
  1494.                                      Str31 theServerName,
  1495.                                      Str27 theVolName,
  1496.                                      Str31 theUserName,
  1497.                                      Str8 theUserPassWord,
  1498.                                      Str8 theVolPassWord,
  1499.                                      MyAFPVolMountInfoPtr theAFPInfo)
  1500. {
  1501.     /* Fill in an AFPVolMountInfo record that can be passed to VolumeMount */
  1502.     theAFPInfo->length = sizeof(MyAFPVolMountInfo);
  1503.     theAFPInfo->media = AppleShareMediaType;
  1504.     theAFPInfo->flags = theFlags;
  1505.     theAFPInfo->nbpInterval = theNBPInterval;
  1506.     theAFPInfo->nbpCount = theNBPCount;
  1507.     theAFPInfo->uamType = theUAMType;
  1508.     theAFPInfo->zoneNameOffset = (short)((long)theAFPInfo->zoneName - (long)theAFPInfo);
  1509.     theAFPInfo->serverNameOffset = (short)((long)theAFPInfo->serverName - (long)theAFPInfo);
  1510.     theAFPInfo->volNameOffset = (short)((long)theAFPInfo->volName - (long)theAFPInfo);
  1511.     theAFPInfo->userNameOffset = (short)((long)theAFPInfo->userName - (long)theAFPInfo);
  1512.     theAFPInfo->userPasswordOffset = (short)((long)theAFPInfo->userPassword - (long)theAFPInfo);
  1513.     theAFPInfo->volPasswordOffset = (short)((long)theAFPInfo->volPassword - (long)theAFPInfo);
  1514.     pcpy(theAFPInfo->zoneName, theZoneName);
  1515.     pcpy(theAFPInfo->serverName, theServerName);
  1516.     pcpy(theAFPInfo->volName, theVolName);
  1517.     pcpy(theAFPInfo->userName, theUserName);
  1518.     pcpy(theAFPInfo->userPassword, theUserPassWord);
  1519.     pcpy(theAFPInfo->volPassword, theVolPassWord);
  1520. }
  1521.  
  1522. /*****************************************************************************/
  1523.  
  1524. pascal    OSErr    RetrieveAFPVolMountInfo(AFPVolMountInfoPtr theAFPInfo,
  1525.                                         short *theFlags,
  1526.                                         short *theUAMType,
  1527.                                         StringPtr theZoneName,
  1528.                                         StringPtr theServerName,
  1529.                                         StringPtr theVolName,
  1530.                                         StringPtr theUserName)
  1531. {
  1532.     /* Retrieve the AFP mounting information from an AFPVolMountInfo record. */
  1533.     if (theAFPInfo->media != AppleShareMediaType)
  1534.         return (paramErr);
  1535.     
  1536.     *theFlags = theAFPInfo->flags;
  1537.     *theUAMType = theAFPInfo->uamType;
  1538.     pcpy(theZoneName, (StringPtr)((long)theAFPInfo + theAFPInfo->zoneNameOffset));
  1539.     pcpy(theServerName, (StringPtr)((long)theAFPInfo + theAFPInfo->serverNameOffset));
  1540.     pcpy(theVolName, (StringPtr)((long)theAFPInfo + theAFPInfo->volNameOffset));
  1541.     pcpy(theUserName, (StringPtr)((long)theAFPInfo + theAFPInfo->userNameOffset));
  1542. }
  1543.  
  1544. /*****************************************************************************/
  1545.  
  1546. pascal    OSErr    GetUGEntries(short objType,
  1547.                              UGEntryPtr entries,
  1548.                              long reqEntryCount,
  1549.                              long *actEntryCount,
  1550.                              long *objID)
  1551. {
  1552.     HParamBlockRec pb;
  1553.     OSErr error = noErr;
  1554.     UGEntry *endEntryArray = entries + reqEntryCount;
  1555.  
  1556.     pb.objParam.ioObjType = objType;
  1557.     *actEntryCount = 0;
  1558.     for (; (entries < endEntryArray) && (error == noErr); ++entries)
  1559.     {
  1560.         pb.objParam.ioObjNamePtr = (StringPtr)entries->name;
  1561.         pb.objParam.ioObjID = *objID;
  1562.         /* Files.h in the universal interfaces, PBGetUGEntrySync takes a CMovePBPtr */
  1563.         /* as the parameter. Inside Macintosh and the original glue used HParmBlkPtr. */
  1564.         /* A CMovePBPtr works OK, but this will be changed in the future  back to */
  1565.         /* HParmBlkPtr, so I'm just casting it here. */
  1566.         error = PBGetUGEntrySync((CMovePBPtr)&pb);
  1567.         if (error == noErr)
  1568.         {
  1569.             entries->objID = *objID = pb.objParam.ioObjID;
  1570.             entries->objType = objType;
  1571.             ++*actEntryCount;
  1572.         }
  1573.     }
  1574.     return (error);
  1575. }
  1576.  
  1577. /*****************************************************************************/
  1578.  
  1579. /*    Desktop Manager calls    */
  1580.  
  1581. /*****************************************************************************/
  1582.  
  1583. pascal    OSErr    CopyComment(short srcVRefNum,
  1584.                             long srcDirID,
  1585.                             StringPtr srcName,
  1586.                             short dstVRefNum,
  1587.                             long dstDirID,
  1588.                             StringPtr dstName)
  1589. /* Both volumes must support the Desktop Manager for this to work */
  1590. {
  1591.     DTPBRec pb;
  1592.     OSErr error;
  1593.     unsigned char commentBuffer[199];
  1594.     long commentLength;
  1595.  
  1596.     pb.ioNamePtr = srcName;
  1597.     pb.ioVRefNum = srcVRefNum;
  1598.     error = PBDTOpenInform(&pb);
  1599.     if (error == paramErr)
  1600.         error = PBDTGetPath(&pb);
  1601.     if (error == noErr)
  1602.     {
  1603.         pb.ioNamePtr = srcName;
  1604.         pb.ioDirID = srcDirID;
  1605.         pb.ioDTBuffer = (Ptr) & commentBuffer;
  1606.         error = PBDTGetCommentSync(&pb);
  1607.         if (error == noErr)
  1608.         {
  1609.             commentLength = pb.ioDTActCount;
  1610.             pb.ioNamePtr = dstName;
  1611.             pb.ioVRefNum = dstVRefNum;
  1612.             error = PBDTOpenInform(&pb);
  1613.             if (error == paramErr)
  1614.                 error = PBDTGetPath(&pb);
  1615.             if (error == noErr)
  1616.             {
  1617.                 pb.ioNamePtr = dstName;
  1618.                 pb.ioDirID = dstDirID;
  1619.                 pb.ioDTBuffer = (Ptr) & commentBuffer;
  1620.                 pb.ioDTReqCount = commentLength;
  1621.                 error = PBDTSetCommentSync(&pb);
  1622.             }
  1623.         }
  1624.     }
  1625.     return (error);
  1626. }
  1627.  
  1628. /*****************************************************************************/
  1629.  
  1630. pascal    OSErr    FSpCopyComment(const FSSpec *srcSpec,
  1631.                                const FSSpec *dstSpec)
  1632. /* Both volumes must support the Desktop Manager for this to work */
  1633. {
  1634.     return (CopyComment(srcSpec->vRefNum, srcSpec->parID, (StringPtr)srcSpec->name,
  1635.                         dstSpec->vRefNum, dstSpec->parID, (StringPtr)dstSpec->name));
  1636. }
  1637.  
  1638. /*****************************************************************************/
  1639.  
  1640. pascal    OSErr    SetComment(short vRefNum,
  1641.                              long dirID,
  1642.                              StringPtr name,
  1643.                              const Str255 comment)
  1644. {
  1645.     DTPBRec pb;
  1646.     OSErr error;
  1647.  
  1648.     pb.ioNamePtr = name;
  1649.     pb.ioVRefNum = vRefNum;
  1650.     error = PBDTOpenInform(&pb);
  1651.     if (error == paramErr)
  1652.         error = PBDTGetPath(&pb);
  1653.     if (error == noErr)
  1654.     {
  1655.         pb.ioNamePtr = name;
  1656.         pb.ioDirID = dirID;
  1657.         pb.ioDTBuffer = (Ptr)&comment[1];
  1658.         pb.ioDTReqCount = comment[0];
  1659.         error = PBDTSetCommentSync(&pb);
  1660.     }
  1661.     return (error);
  1662. }
  1663.  
  1664. /*****************************************************************************/
  1665.  
  1666. pascal    OSErr    FSpSetComment(const FSSpec *spec,
  1667.                               const Str255 comment)
  1668. {
  1669.     return (SetComment(spec->vRefNum, spec->parID, (StringPtr)spec->name, comment));
  1670. }
  1671.  
  1672. /*****************************************************************************/
  1673.  
  1674. pascal    OSErr    GetComment(short vRefNum,
  1675.                            long dirID,
  1676.                            StringPtr name,
  1677.                            StringPtr comment)
  1678. {
  1679.     DTPBRec pb;
  1680.     OSErr error;
  1681.  
  1682.     if (comment == nil)
  1683.         return (paramErr);
  1684.     
  1685.     pb.ioNamePtr = name;
  1686.     pb.ioVRefNum = vRefNum;
  1687.     error = PBDTOpenInform(&pb);
  1688.     if (error == paramErr)
  1689.         error = PBDTGetPath(&pb);
  1690.     if (error == noErr)
  1691.     {
  1692.         pb.ioNamePtr = name;
  1693.         pb.ioDirID = dirID;
  1694.         pb.ioDTBuffer = (Ptr)&comment[1];
  1695.         error = PBDTGetCommentSync(&pb);
  1696.         if (error == noErr)
  1697.             comment[0] = (unsigned char)pb.ioDTActCount;
  1698.         else
  1699.             comment[0] = 0;
  1700.     }
  1701.     return (error);
  1702. }
  1703.  
  1704. /*****************************************************************************/
  1705.  
  1706. pascal    OSErr    FSpGetComment(const FSSpec *spec,
  1707.                               StringPtr comment)
  1708. {
  1709.     return (GetComment(spec->vRefNum, spec->parID, (StringPtr)spec->name, comment));
  1710. }
  1711.  
  1712. /*****************************************************************************/
  1713.  
  1714.